{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "8fc29108",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import scipy.io\n",
    "import numpy as np\n",
    "import pywt\n",
    "import wfdb\n",
    "import matplotlib.pyplot as plt\n",
    "import scipy.signal as sg\n",
    "import cv2\n",
    "from pathlib import Path\n",
    "from glob import glob\n",
    "from concurrent.futures import ProcessPoolExecutor\n",
    "import joblib"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "b655bb72",
   "metadata": {},
   "outputs": [],
   "source": [
    "ROOT_PATH = \"E:/pv/WORKING/ECG_main_folder/ECG_Classification/data/raw/mit-bih-arrhythmia-database-1.0.0\"\n",
    "OUTPUT_PATH = \"./processed_images\"\n",
    "sampling_rate = 360\n",
    "wavelet = \"mexh\"  # mexh, morl, gaus8, gaus4\n",
    "scales = pywt.central_frequency(wavelet) * sampling_rate / np.arange(1, 101, 1)\n",
    "\n",
    "# Danh sách tất cả file record (.dat)\n",
    "record_files = sorted(glob(os.path.join(ROOT_PATH, '*.dat')))\n",
    "\n",
    "cpus = 22 if joblib.cpu_count() > 22 else joblib.cpu_count() - 1  # for multi-process"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "efe4adbb",
   "metadata": {},
   "outputs": [],
   "source": [
    "def read_ecg_record(record):\n",
    "    record_path = Path(ROOT_PATH)/ str(record) # Combine PATH and record using pathlib\n",
    "    signal = wfdb.rdrecord(record_path.as_posix(), channels=[0]).p_signal[:, 0]\n",
    "    annotation = wfdb.rdann(record_path.as_posix(), extension=\"atr\")\n",
    "    r_peaks, labels = annotation.sample, np.array(annotation.symbol)\n",
    "    return signal,r_peaks,labels "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "b1512c69",
   "metadata": {},
   "outputs": [],
   "source": [
    "def median_filter(signal):\n",
    "    baseline = sg.medfilt(sg.medfilt(signal, int(0.2 * sampling_rate) - 1), int(0.6 * sampling_rate) - 1)\n",
    "    filtered_signal = signal - baseline \n",
    "    return filtered_signal"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "2fe9a8b8",
   "metadata": {},
   "outputs": [],
   "source": [
    "def remove_invalid_labels(r_peaks, labels):\n",
    "    # Các nhãn không phải nhịp tim\n",
    "    invalid_labels = ['|', '~', '!', '+', '[', ']', '\"', 'x']\n",
    "    indices = [i for i, label in enumerate(labels) if label not in invalid_labels]\n",
    "        #enumerate(labels) sẽ tạo ra một danh sách các cặp (index, label)\n",
    "        #labels = ['N', '|', 'V', '~'] → enumerate(labels) sinh ra (0, 'N'), (1, '|'), (2, 'V'), (3, '~')\n",
    "        #Vòng lặp for i, label in enumerate(labels)\n",
    "        #Điều kiện if label not in invalid_labels\n",
    "        #  ==> Danh sách kết quả indices Chứa danh sách các chỉ số i của label hợp lệ\n",
    "    r_peaks, labels = r_peaks[indices], labels[indices]\n",
    "    return r_peaks, labels"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "3117554a",
   "metadata": {},
   "outputs": [],
   "source": [
    "def align_r_peaks(r_peaks, filtered_signal,tol=0.05):\n",
    "    newR = []\n",
    "        #danh sách rỗng newR để lưu vị trí đỉnh R đã được căn chỉnh\n",
    "    for r_peak in r_peaks: #Lặp qua từng điểm đỉnh R (r_peak) đã phát hiện trước đó.\n",
    "        r_left = np.maximum(r_peak - int(tol * sampling_rate), 0)\n",
    "            #Xác định giới hạn trái: Lùi lại một khoảng tol * sampling_rate từ vị trí r_peak để tạo một cửa sổ tìm kiếm.\n",
    "            #np.maximum(..., 0) để đảm bảo không bị âm (tránh lỗi khi r_peak ở đầu tín hiệu).\n",
    "        r_right = np.minimum(r_peak + int(tol * sampling_rate), len(filtered_signal))\n",
    "            #Xác định giới hạn phải\n",
    "        newR.append(r_left + np.argmax(filtered_signal[r_left:r_right]))\n",
    "    r_peaks = np.array(newR, dtype=\"int\") # ép kiểu newR về int \n",
    "\n",
    "    #normalize signal \n",
    "    normalized_signal = filtered_signal / np.mean(filtered_signal[r_peaks])\n",
    "    return r_peaks, normalized_signal"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "5f2ee217",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Nhóm các label thành 5 class\n",
    "def AAMI_categories(labels): \n",
    "    AAMI = {\n",
    "        \"N\": 0, \"L\": 0, \"R\": 0, \"e\": 0, \"j\": 0,  # N\n",
    "        \"A\": 1, \"a\": 1, \"S\": 1, \"J\": 1,  # SVEB\n",
    "        \"V\": 2, \"E\": 2,  # VEB\n",
    "        \"F\": 3,  # F\n",
    "        \"/\": 4, \"f\": 4, \"Q\": 4  # Q\n",
    "    }\n",
    "    categories = [AAMI[label] for label in labels]\n",
    "        #Nếu label = \"N\", thì AAMI[\"N\"] sẽ trả về 0\n",
    "        #[AAMI[label] for label in labels] là list comprehension trong Python, nó tạo ra một list mới. \n",
    "        #Kết quả là một danh sách các giá trị mã phân loại tương ứng với từng nhãn trong labels.\n",
    "    return categories"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "id": "95846336",
   "metadata": {},
   "outputs": [],
   "source": [
    "# hàm này returns:\n",
    "    # beats (list of np.ndarray): Danh sách đoạn nhịp ECG đã cắt\n",
    "    # beat_labels (list): Nhãn tương ứng với từng nhịp\n",
    "def segmentation(normalize_signal, r_peaks, categories):\n",
    "    before, after = 90, 110 ## Lấy đoạn tín hiệu 200ms quanh R-peak\n",
    "    beats, beat_labels = [], []\n",
    "    for r, category in zip(r_peaks, categories):\n",
    "        start = r - before\n",
    "        end = r + after\n",
    "        if category!=4:\n",
    "            if start >= 0 and end < len(normalize_signal): # nếu không đủ dữ liệu (gần biên), bỏ qua\n",
    "                beat = normalize_signal[start:end]\n",
    "                beats.append(beat)\n",
    "                beat_labels.append(category)\n",
    "        \n",
    "    return beats, beat_labels\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "322bf4ad",
   "metadata": {},
   "outputs": [],
   "source": [
    "from tqdm import tqdm #tqdm dùng để hiện progress bar khi xử lý nhiều ảnh\n",
    "from PIL import Image\n",
    "from concurrent.futures import ProcessPoolExecutor\n",
    "from functools import partial\n",
    "\n",
    "def CWT(record_name, beats, beat_labels, image_size = 100):\n",
    "\n",
    "    os.makedirs(OUTPUT_PATH, exist_ok=True) #Tạo thư mục gốc để lưu ảnh (nếu chưa tồn tại). Dùng exist_ok=True để tránh lỗi nếu thư mục đã có\n",
    "    \n",
    "    for i, (beat, beat_label) in enumerate(tqdm(zip(beats, beat_labels), total=len(beats))):\n",
    "            #zip(beats, beat_labels)\tGộp hai list beats và beat_labels lại thành các cặp (beat, label)\n",
    "            #tqdm(...)\tHiển thị progress bar\n",
    "\n",
    "        # Chuyển CWT\n",
    "        # 1. Tính CWT → scalogram\n",
    "        coef, _ = pywt.cwt(beat, scales, wavelet)\n",
    "        scalogram = np.abs(coef)\n",
    "        # 2. Chuẩn hóa về [0, 255] để lưu ảnh\n",
    "        scalogram -= np.min(scalogram)\n",
    "        scalogram /= np.max(scalogram) + 1e-6 #+1e-6: tránh chia cho 0\n",
    "        scalogram *= 255\n",
    "        scalogram = scalogram.astype(np.uint8) #Đổi sang uint8: đúng định dạng ảnh grayscale.\n",
    "        # 3. Resize ảnh\n",
    "        resized = cv2.resize(scalogram, (image_size, image_size), interpolation=cv2.INTER_CUBIC) #dùng nội suy INTER_CUBIC để giữ chất lượng cao\n",
    "        # 4. Tạo thư mục theo label\n",
    "        label_dir = os.path.join(OUTPUT_PATH, str(beat_label))\n",
    "        os.makedirs(label_dir, exist_ok=True)\n",
    "        # 5. Lưu ảnh dưới dạng .png (grayscale)\n",
    "        save_path = os.path.join(label_dir, f'{record_name}_{beat_label}_{i}.png')\n",
    "        cv2.imwrite(save_path, resized)  # ảnh grayscale\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "05d2931b",
   "metadata": {},
   "source": [
    "### thử trên 1 record 100"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "ee6808e5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([-0.145, -0.145, -0.145, ..., -0.675, -0.765, -1.28 ],\n",
       "      shape=(650000,))"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "signal100,r_peaks100,labels100 = read_ecg_record(100)       \n",
    "signal100"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "6477c7e9",
   "metadata": {},
   "outputs": [],
   "source": [
    "filtered100 = median_filter(signal100)\n",
    "# print(filtered100)\n",
    "# plt.plot(filtered100[:1000])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "3bd450d4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array(['N', 'N', 'N', ..., 'N', 'N', 'N'], shape=(2273,), dtype='<U1')"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "r_peaks100, labels100 = remove_invalid_labels(r_peaks100,labels100)\n",
    "r_peaks100\n",
    "labels100"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "7e71e82d",
   "metadata": {},
   "outputs": [],
   "source": [
    "r_peaks100, normalize_signal100 = align_r_peaks(r_peaks100,filtered100)\n",
    "#plt.plot(normalize_signal100[:1000])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "61670838",
   "metadata": {},
   "outputs": [],
   "source": [
    "categories100 = AAMI_categories(labels100)\n",
    "#categories100"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "80eeeb35",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[-0.01850215 -0.00740086 -0.00740086 ... -0.499558   -0.56616573\n",
      " -0.94730998] 650000\n",
      "[    77    370    663 ... 649485 649734 649991] 2273\n",
      "2273\n"
     ]
    }
   ],
   "source": [
    "# sau khi đã filter \n",
    "print(normalize_signal100 , len(normalize_signal100))\n",
    "print(r_peaks100 , len(r_peaks100))\n",
    "print(len(categories100))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c1383a7a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2271\n"
     ]
    }
   ],
   "source": [
    "beats100, beat_labels100 = segmentation(normalize_signal100,r_peaks100,categories100)\n",
    "#print(len(beats100))\n",
    "#print(len(beat_labels100))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "8f5b8b89",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2271/2271 [00:06<00:00, 338.17it/s]\n"
     ]
    }
   ],
   "source": [
    "CWT(beats100, beat_labels100)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "21d37cf9",
   "metadata": {},
   "source": [
    "### Hàm MAIN"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 112,
   "id": "b3cfb577",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# for record_file in tqdm(record_files):\n",
    "#         record_name = os.path.splitext(os.path.basename(record_file))[0]\n",
    "#         print(f'\\n Processing record: {record_name}')\n",
    "\n",
    "#         # 1. Load dữ liệu\n",
    "#         signal,r_peaks,labels = read_ecg_record(record_name) \n",
    "#         # 2. Lọc tín hiệu\n",
    "#         filtered_signal = median_filter(signal)\n",
    "#         # 3. lọc nhãn invalid\n",
    "#         r_peaks, labels = remove_invalid_labels(r_peaks,labels)\n",
    "#         # 4. căn chỉnh R, chuẩn hóa\n",
    "#         r_peaks, normalize_signal = align_r_peaks(r_peaks,filtered_signal)\n",
    "#         # 5. đổi sang dạng AAMI\n",
    "#         categories = AAMI_categories(labels)\n",
    "#         # 6. segmentation\n",
    "#         beats, beat_labels = segmentation(normalize_signal,r_peaks,categories)\n",
    "#         # 7. Chuyển thành scalogram và lưu\n",
    "#         CWT(beats, beat_labels)\n",
    "# print('\\n✅ Hoàn thành xử lý toàn bộ records!')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "81f7d196",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 100\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2271/2271 [00:17<00:00, 131.63it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 101\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 1862/1862 [00:13<00:00, 138.30it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 103\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2084/2084 [00:15<00:00, 138.36it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 105\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2567/2567 [00:19<00:00, 132.52it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 106\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2027/2027 [00:16<00:00, 124.09it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 108\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 1763/1763 [00:13<00:00, 127.74it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 109\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2531/2531 [00:20<00:00, 126.17it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 111\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2124/2124 [00:16<00:00, 125.98it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 112\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2539/2539 [00:18<00:00, 140.45it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 113\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 1794/1794 [00:12<00:00, 142.72it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 114\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 1879/1879 [00:13<00:00, 143.24it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 115\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 1952/1952 [00:13<00:00, 143.25it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 116\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2411/2411 [00:17<00:00, 139.31it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 117\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 1534/1534 [00:10<00:00, 143.16it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 118\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2277/2277 [00:15<00:00, 142.74it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 119\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 1987/1987 [00:14<00:00, 141.76it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 121\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 1863/1863 [00:13<00:00, 139.38it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 122\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2475/2475 [00:17<00:00, 142.81it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 123\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 1517/1517 [00:10<00:00, 142.41it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 124\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 1619/1619 [00:11<00:00, 141.92it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 200\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2600/2600 [00:19<00:00, 134.63it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 201\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 1963/1963 [00:13<00:00, 141.32it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 202\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2136/2136 [00:14<00:00, 142.61it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 203\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2976/2976 [00:21<00:00, 141.57it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 205\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2656/2656 [00:18<00:00, 142.39it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 207\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 1859/1859 [00:13<00:00, 142.64it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 208\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2951/2951 [00:21<00:00, 138.60it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 209\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 3005/3005 [00:21<00:00, 141.37it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 210\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2648/2648 [00:18<00:00, 141.88it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 212\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2747/2747 [00:19<00:00, 140.74it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 213\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 3250/3250 [00:22<00:00, 141.43it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 214\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2258/2258 [00:16<00:00, 140.45it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 215\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 3363/3363 [00:23<00:00, 141.21it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 219\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2154/2154 [00:15<00:00, 140.02it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 220\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2046/2046 [00:14<00:00, 141.21it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 221\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2427/2427 [00:17<00:00, 136.91it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 222\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2482/2482 [00:17<00:00, 142.61it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 223\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2605/2605 [00:18<00:00, 140.57it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 228\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2053/2053 [00:14<00:00, 140.36it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 230\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2255/2255 [00:15<00:00, 142.97it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 231\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 1571/1571 [00:11<00:00, 142.74it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 232\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 1780/1780 [00:12<00:00, 140.52it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 233\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 3077/3077 [00:21<00:00, 141.63it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Processing record: 234\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2753/2753 [00:20<00:00, 134.24it/s]\n"
     ]
    }
   ],
   "source": [
    "all_records = [\n",
    "        '100', '101', '103', '105', '106', '108', '109', '111', '112', '113',\n",
    "        '114', '115', '116', '117', '118', '119', '121', '122', '123', '124',\n",
    "        '200', '201', '202', '203', '205', '207', '208', '209', '210', '212',\n",
    "        '213', '214', '215', '219', '220', '221', '222', '223', '228', '230',\n",
    "        '231', '232', '233', '234'\n",
    "    ]\n",
    "\n",
    "for record_name in all_records:\n",
    "\n",
    "    print(f'\\n Processing record: {record_name}')\n",
    "\n",
    "    # 1. Load dữ liệu\n",
    "    signal,r_peaks,labels = read_ecg_record(record_name) \n",
    "    # 2. Lọc tín hiệu\n",
    "    filtered_signal = median_filter(signal)\n",
    "    # 3. lọc nhãn invalid\n",
    "    r_peaks, labels = remove_invalid_labels(r_peaks,labels)\n",
    "    # 4. căn chỉnh R, chuẩn hóa\n",
    "    r_peaks, normalize_signal = align_r_peaks(r_peaks,filtered_signal)\n",
    "    # 5. đổi sang dạng AAMI\n",
    "    categories = AAMI_categories(labels)\n",
    "    # 6. segmentation\n",
    "    beats, beat_labels = segmentation(normalize_signal,r_peaks,categories)\n",
    "    # 7. Chuyển thành scalogram và lưu\n",
    "    CWT(record_name,beats, beat_labels)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b6ee262b",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "ecg_env",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
